/*
 * Copyright 2003 Program of Computer Graphics, Cornell University
 *     580 Rhodes Hall
 *     Cornell University
 *     Ithaca NY 14853
 * Web: http://www.graphics.cornell.edu/
 * 
 * Not for commercial use. Do not redistribute without permission.
 */

#ifndef _H_GEOMETRY
#define _H_GEOMETRY

#include "Ray.h"
#include "AxisAlignedBox.h"
#include "PrimitiveList.h"
#include "ExpandableArray.h"
#include "KDSplits.h"
#include "PoolAllocator.h"
#include "BriteDefs.h"
#include <iostream>



//geometry types
#define GEOM_TRIANGLE             1
#define GEOM_PARALLELOGRAM        2
#define GEOM_DISK                 3
#define GEOM_SPHERE               4
//packed or compiled types
#define GEOM_QUADPACKED_TRIANGLE  128
#define GEOM_PACKED_PARALLELOGRAM 129
#define GEOM_PACKED_DISK          130
#define GEOM_PACKED_SPHERE        131

//we reserve the top four bits (of 32) in geometry ids for storing flags
//all valid ids are positive
//objects with transparency can be flagged for special handling
//the fp bit can be set so that the stored id does not look like a denormal
#define GID_VALUE_MASK       0x0FFFFFFF
#define GID_FLAG_MASK        0xF0000000
#define GID_FLAG_TRANSPARENT 0x10000000
#define GID_FLAG_FP          0x40000000
#define GID_INVALID          0x80000000


//class for general geometry object (or group of geometry)
class ALIGN16 GeometryObject {
public:
  GeometryObject() {}
  virtual ~GeometryObject() {}
  //override new and delete to force proper alignement
  static void* operator new (size_t size) { 
    BRITE_DEBUGBREAK;  //do not use new operator as it allocates more memory than is needed
    //instead objects should be pool allocated
    return malloc_aligned(static_cast<int>(size),16); 
  }
  static void operator delete (void *p) { 
    BRITE_DEBUGBREAK;  //do not use delete operator since we are not using the new operator
    free_aligned(p); 
  }
  static void* operator new (size_t size, void *place) { 
    return place;
  }
  static void operator delete (void *p, void *place) { 
    BRITE_DEBUGBREAK;  //should never happen
  }

  virtual int getType() const=0;  //each GeometryObject subclass must have a unique type
  //Compile array of GeometryObjects (all will be of my type) into a PrimitiveList
  virtual PrimitiveList *compile(int num,const GeometryObject **array,PoolAllocator *pool)const=0;
  //check if this object (potentially) intersects the given bounding box (tries to be exact)
  virtual bool intersectsBox(const AxisAlignedBox &box)const=0;
  //enlarge the given bounding box to also enclose this object
  virtual void expandBoxToInclude(AxisAlignedBox *box)const=0;
  //estimate the bounding box of the portion of the object inside the specified bounding box
  virtual AxisAlignedBox estimateIntersectionBounds(const AxisAlignedBox &box)const=0;
  //return approximate cost relative to a triangle
  virtual int estimateRelativeCost()const=0;
};

//sets of geometry objects.  Used to keep track of geometry within a cell or tree node
class GeometrySet {
  //we special case triangles, because they are our most common and default case
  ExpandableArray<class Triangle *> tris;
  ExpandableArray<GeometryObject*> others;
public:
  GeometrySet();
  GeometrySet(const GeometrySet &copy, bool copyContents);
  ~GeometrySet();

  void addTriangle(Triangle *obj) { tris.append(obj); }
  void add(GeometryObject *obj) { 
    if (obj->getType() == GEOM_TRIANGLE) tris.append((Triangle*)obj);
    else others.append(obj);
  }
  int size() const { return tris.size() + others.size(); }
  int numTriangles() const { return tris.size(); }
  int numOthers() const { return others.size(); }
  void removeAll() { tris.removeAll(); others.removeAll(); } //clears but does not deallocate anything
  void copyFrom(const GeometrySet &copy)
  { tris.copyFrom(copy.tris); others.copyFrom(copy.others); }
  void expandBoxToInclude(AxisAlignedBox *box) const;
  void setSubsetIntersectsBox(const GeometrySet &set, const AxisAlignedBox &box);
  PrimitiveList *compileToPrimitiveList(PoolAllocator *pool);
  void estimateFixedSplits(KDFixedSplitEstimator &splits) const;
  void estimateAllSplits(KDAllSplitEstimator &splits) const;
/*
  void debug_intersect_all(const Ray &ray, IntersectionRecord *hitRec) {
    Ray fRay(ray);
    for(int i=0;i<tris.size();i++) {
      if (tris[i]->intersectsRay(fRay,hitRec)) {
        fRay.tEnd = hitRec->t;
      }
    }
  }
*/
};

#endif /* _H_GEOMETRY */

